home *** CD-ROM | disk | FTP | other *** search
/ PC Media 22 / PC MEDIA CD22.iso / share / prog / datalib2 / index2.cpp < prev    next >
C/C++ Source or Header  |  1995-08-14  |  9KB  |  353 lines

  1. #include "datapriv.hpp"
  2.  
  3. /*
  4.   This file contains the routines concerned with the generation and use
  5.   of index files, and contains the routines for writing a new or changed
  6.   record, and finding a record in the index file
  7. */
  8.  
  9. /****************************************************************************
  10. *                                        *
  11. * The following routines are all coupled to records                         *
  12. *                                        *
  13. ****************************************************************************/
  14.  
  15. /********************************************************************
  16.  
  17.  Index Creation Stuff
  18.  
  19. *********************************************************************/
  20.  
  21. // Check an index expression and return its length
  22.  
  23. int record::indchk(char *ex,int &rtype)
  24. {
  25.  char *wsp;
  26.  op *rv;
  27.  
  28.  if (!db->ex)
  29.  {
  30.   db->ex=new expval(db);
  31.   db->exwork=new char[512];
  32.  }
  33.  if (!db->ex || !db->exwork || db->ex->erflag==2) return 0;
  34.  db->ex->erflag=0;
  35.  
  36.  wsp=db->exwork;
  37.  db->ex->exetoken(ex,&wsp);
  38.  if (db->ex->erflag==1) {db->ex->erflag=3; return 0;}    // show tokeniser error
  39.  
  40.  wsp=db->exwork;
  41.  indflg=1;
  42.  rv=db->ex->exeval(&wsp,this);
  43.  if (db->ex->erflag) return 0;
  44.  
  45.  rtype=rv->optype;
  46.  if (rtype==OPINT) return 8;
  47.  if (rtype==OPSTR)
  48.  {
  49.   if (rv->oplen<1) strcpy(db->ers,"Index length must be greater than 0");
  50.   if (rv->oplen>100) strcpy(db->ers,"Index length must be 100 or less");
  51.   return (rv->oplen>0 && rv->oplen<101) ? rv->oplen : 0;
  52.  }
  53.  if (rtype==OPDATE) return 8;
  54.  strcpy(db->ers,"No Logical expr. in index"); return 0; // Must be logical
  55. }
  56.  
  57. /********************* RECORD::GETIND ************************/
  58.  
  59. // Get the specified index record, we know already that there is one
  60. // 'cos the calling function has checked the bounds.
  61.  
  62. long record::getind(long n)
  63. {
  64.  long ln=n;            // Local copy of n
  65.  struct indpt *npt;        // Useful pointer
  66.  int dirn=1;            // + or - 1 depending on next/previous
  67.  
  68.  if (n==FIRST || n==LAST)    // Return to top level indclus
  69.  {
  70.   while(curind->icp->parent)
  71.   {
  72.    npt=curind; 
  73.    curind=curind->parent; 
  74.    delete npt;
  75.   }
  76.   if (n==FIRST)
  77.        {ln=NEXT; curind->currec=-1;}    // FIRST
  78.   else
  79.        {curind->currec=curind->icp->nclusrec; ln=PREVIOUS;}    // LAST
  80.  }
  81.  
  82.  if (ln==PREVIOUS) dirn=-1;
  83.  
  84.  while (1)                // Select next record
  85.  {
  86.   if (!curind->icp->clustyp)        // Is this a record cluster ?
  87.   {
  88.    if ((ln==NEXT && curind->currec<curind->icp->nclusrec-1) ||
  89.        (ln==PREVIOUS && curind->currec>0))          // Get next record
  90.    {
  91.     curind->currec+=dirn;
  92.     long rv=*(long *)(curind->icp->clusdat+8+curind->currec*curind->icp->reclen);
  93.     return(rv);
  94.    }
  95.    else                    // Run out of records in this cluster
  96.    {
  97.     if (!curind->parent) return 0;
  98.     npt=curind->parent; delete curind;    // Get parent record
  99.     curind=npt;
  100.    }
  101.   }
  102.   else        // We have a pointer cluster
  103.   {
  104.    long nclus;
  105.  
  106.    if ((ln==NEXT && curind->currec<curind->icp->nclusrec-1) ||
  107.        (ln==PREVIOUS && curind->currec>0))           // Get next record
  108.    {
  109.     curind->currec+=dirn;
  110.     nclus=*(long *)(curind->icp->clusdat+4+curind->currec*curind->icp->reclen);
  111.     curind=new indpt(nclus,curind,curind->icp->oi);
  112.     if (ln==PREVIOUS) curind->currec=curind->icp->nclusrec;
  113.    }
  114.    else     // Go back to parent
  115.    {
  116.     if (!curind->parent)
  117.     {
  118.      if (ln==NEXT) getind(LAST); else getind(FIRST);
  119.      return 0;
  120.     }
  121.     npt=curind->parent; delete curind;
  122.     curind=npt;
  123.    }
  124.   }
  125.  }
  126. }
  127.  
  128.  
  129. /********************** SELKEY ********************************/
  130.  
  131. // Select record by key
  132.  
  133. int record::selkey(double value,int type)
  134. {
  135.  char ws[10];
  136.  
  137.  *(double *)ws=value;
  138.  
  139.  return selkey(ws,type,OPINT);
  140. }
  141.  
  142. // Character or Double, flag shows which
  143. // Note in this function if type is READIND then the record is not read
  144. // from disk,l this is used purely to update the index pointers for a
  145. // newly written record
  146.  
  147. int record::selkey(char *value,int type,int num)
  148. {
  149.  int i;
  150.  int ret=-1;
  151.  
  152.  if (!curind) return CANTSEL;
  153.  memcpy(lkey,value,curind->icp->explen+1);
  154.  ltype=type;
  155.  lseltype=num;
  156.  
  157.  indpt *npt;        // Now go back up to top level index
  158.  
  159.  while(curind->parent) {npt=curind; curind=curind->parent; delete npt;}
  160.  delete curind;
  161.  curind=new indpt(oi->topind);
  162.  
  163.  curind=curind->selkey(value,num,ret,i);
  164.  rn=*(long *)(curind->icp->clusdat+8+i*curind->icp->reclen);
  165.  if (type!=READIND) select(rn,ALL,1);
  166.  
  167.  while((type==DEL && *recbuf==' ') || (type==NOTDEL && *recbuf=='*'))
  168.  {
  169.   if (ret=selkey()) return ret;
  170.  }
  171.  return ret;
  172. }
  173.  
  174. // Get the next record which matches the current record key
  175.  
  176. int record::selkey(void)
  177. {
  178.  char *keyp;
  179.  
  180.  if (ltype==-1) return CANTSEL;
  181.  
  182.  do
  183.  {
  184.   long tn;
  185.  
  186.   if (!(tn=getind(NEXT))) return CANTSEL;
  187.  
  188.   keyp=curind->icp->clusdat+(curind->currec)*curind->icp->reclen+12;
  189.   if ((lseltype!=OPINT && strcmpdb(keyp,lkey,curind->icp->explen)) ||
  190.       (lseltype==OPINT && *(double *)lkey-*(double *)keyp)) return NOKEY;
  191.  
  192.   rn=tn;
  193.   select(rn,ALL,1);
  194.  }
  195.  while((ltype==DEL && *recbuf==' ') || (ltype==NOTDEL && *recbuf=='*'));
  196.  
  197.  return 0;
  198. }
  199.  
  200. // Delete a key from the record
  201. // Use a new record to find this records key in the index file, when
  202. // we find it delete the key
  203.  
  204. void record::delkey(char *oldkey,index *ip)
  205. {
  206.  record rec(*db,ip->name);        // Create record used to search index file
  207.  rec.selkey(oldkey,ALL,ip->indtype);    // Now find this record in the index file
  208.  while(rec.rn!=rn)
  209.  {
  210.   if (rec.selkey())
  211.   {
  212.    dbfer(NODELKEY);
  213.    return;
  214.   }
  215.  }
  216.  rec.curind->icp->subtract(rec.curind->currec);    // Delete key from tree
  217. }
  218.  
  219. // Insert a new key from the current record
  220.  
  221. void record::inskey(char *newkey,index *ip)
  222. {
  223.  int rsk;    // Return value from selkey
  224.  int irn;    // Record value found by selkey
  225.  
  226.  indpt *clin=new indpt(ip->tblk,0,ip);        // Cluster to insert key
  227.  clin=clin->selkey(newkey,ip->indtype,rsk,irn);    // Find nearest key in index
  228.  
  229.  clin->icp->add(newkey,rn,irn,rsk);    // Add the new key
  230.  
  231.  indpt *nin;
  232.  while(clin->parent) {nin=clin->parent; delete clin; clin=nin;} delete clin;
  233. }
  234.  
  235. /**************************************************************************
  236.  
  237.  Routines concerned with the index structure
  238.  
  239. **************************************************************************/
  240.  
  241. index::~index()
  242. {
  243.  if (chng)
  244.  {
  245.   *(long *)fclus=tblk;
  246.   *(long *)(fclus+4)=lblk;
  247.   Fseek(fp,0,SEEK_SET);
  248.   Fwrite(fclus,512,1,fp);
  249.  }
  250.  
  251.  indclus *ip,*nip;
  252.  
  253.  ip=topind; while(ip) {nip=ip->next; delete ip; ip=nip;}
  254.  fclose(fp);
  255. }
  256.  
  257. /***************** Add an index to the database file ********************/
  258.  
  259. int database::addindex(char *iname)
  260. {
  261.  index *ip,*nip,*lip;
  262.  char ws[128],*fcp,*wsp;
  263.  FILE *fp;
  264.  
  265.  strcpy(ws,iname);
  266.  if (wsp=strchr(ws,'.')) *wsp=0; strcat(ws,".NDX");
  267.  if (!(fp=fopen(ws,"r+b"))) return NOINDEX;
  268.  
  269.  *strchr(ws,'.')=0;
  270.  if (!(wsp=strrchr(ws,'\\'))) wsp=ws; else wsp++;
  271.  strupr(wsp);
  272.  if (getindex(wsp)) {delete ip; return INVIND;}
  273.  
  274.  ip=new index;
  275.  ip->next=0;
  276.  ip->fp=fp;
  277.  strcpy(ip->fname,iname);    // Open files & enter names
  278.  strcpy(ip->name,wsp);
  279.  
  280.  fcp=ip->fclus;
  281.  Fread(fcp,512,1,fp);        /// Read the header cluster
  282.  ip->nclus=0;
  283.  ip->topind=0;
  284.  ip->topind=new indclus(*(long *)fcp,0,ip);
  285.  
  286.  ip->topind->reclen=*(int *)(fcp+INDLEN);    // Length of index
  287.  ip->topind->explen=*(int *)(fcp+INDELEN);    // Expression length
  288.  ip->tblk=*(long *)(fcp);            // Top Block
  289.  ip->lblk=*(long *)(fcp+4);            // Last Block
  290.  ip->maxclusrec=*(long *)(fcp+14);        // Max records/cluster
  291.  ip->firstrec=0;                // First record using cluster
  292.  ip->chng=0;                    // Change flag
  293.  strcpy(ip->indexp,fcp+INDEXP);             // Index expression
  294.  if (!ex)
  295.  {
  296.   ex=new expval(this);
  297.   exwork=new char[512];
  298.  }
  299.  if (!ex || !exwork || ex->erflag==2) return NOINDEX;
  300.  wsp=ip->tindexp;
  301.  ex->erflag=0;
  302.  ex->exetoken(fcp+INDEXP,&wsp);        // Tokenise it
  303.  if (ex->erflag)
  304.  {
  305.   if (ex->erflag==1) ex->erflag=3;    // Invalid index expression
  306.   delete ip;
  307.   return INVIND;
  308.  }
  309.  ip->indtype=(*(int *)(fcp+INDTYPE)) ?  OPINT : OPSTR; // Index type
  310.  switch(*(fcp+9))
  311.  {
  312.   case 'N' : ip->restype=OPINT; break;
  313.   case 'D' : ip->restype=OPDATE; break;
  314.   case 'C' : ip->restype=OPSTR; break;
  315.   default  : dbfer(INVIND); break;
  316.  }
  317.  
  318.  // Finally we got here so link the index into the tree
  319.  
  320.  if (!findex) findex=ip;        // First index
  321.  else
  322.  {
  323.   lip=findex; nip=findex->next;
  324.   while(nip) {lip=nip; nip=lip->next;}     // Add to end of tree
  325.   lip->next=ip;
  326.  }
  327.  
  328.  return 0;
  329. }
  330.  
  331. // Remove an index from this database
  332.  
  333. int database::subindex(char *name)
  334. {
  335.  index *nip,*lip,*ip;
  336.  
  337.  if (!(ip=getindex(name))) return NOINDEX;
  338.  
  339.  if (ip==findex) findex=ip->next;
  340.  else
  341.  {
  342.   lip=findex;
  343.   nip=findex->next;
  344.  
  345.   while(nip!=ip) {lip=nip; nip=lip->next;}
  346.   lip->next=nip->next;
  347.  }
  348.  
  349.  delete ip;
  350.  return 0;
  351. }
  352.  
  353.